package com.veskogeorgiev.farm;

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Toolkit;
import java.awt.Desktop.Action;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.prefs.Preferences;

import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;

import com.melloware.jintellitype.HotkeyListener;
import com.melloware.jintellitype.JIntellitype;

public class FarmGui
		implements
			ActionListener,
			PropertyChangeListener,
			WindowListener,
			ChangeListener,
			HotkeyListener {

	protected static final String APP_TITLE = "FarmVille AgroBot";
	protected static final String APP_VERSION = "0.47";
	protected static final long serialVersionUID = 1384886589326956308L;

	protected static final String[] FARM_ITEMS = {"Land", "Tree", "Bale",
			"Cow", "Swan", "Chicken", "Sheep"};
	protected static final String[] BALE_ACTIONS = {"Put", "Sell"};
	protected static final String[] TREE_ACTIONS = {"Collect", "Sell"};
	protected static final String[] LAND_ACTIONS = {"Harvest", "Plow", "Seed",
			"Clean"};
	protected static final String[] COW_ACTIONS = {"Collect", "Sell"};
	protected static final String[] SWAN_ACTIONS = {"Collect", "Sell"};
	protected static final String[] CHICKEN_ACTIONS = {"Collect", "Sell"};
	protected static final String[] SHEEP_ACTIONS = {"Collect", "Sell"};
	protected static final Integer[] ZOOM_LEVELS = {1, 2, 3};
	protected static final Integer[] DELAYS = {100, 200, 500, 1000, 2000, 5000};

	private final JLabel ROWS_LABEL = new JLabel("Rows:");
	private final JLabel COLS_LABEL = new JLabel("Cols:");
	private final JLabel STATUS_LABEL = new JLabel("Status:");
	private final JLabel ZOOM_LABEL = new JLabel("Zoom:");

	static final int ACTION_KEY = KeyEvent.VK_CAPS_LOCK;

	private final SpinnerModel ROWS_SPINNER_MODEL = new SpinnerNumberModel(1,
			1, 600, 1);
	private final SpinnerModel COLS_SPINNER_MODEL = new SpinnerNumberModel(1,
			1, 600, 1);

	private JFrame frame;
	private JButton startButton;
	private JComboBox farmItemsList, actionsList, zoomList;
	private JSpinner rowsText, colsText;
	private JProgressBar progressBar;
	private JLabel remainingLabel, acceptPositionLabel, clickDelayLabel;
	private JPanel statusBar, contentPanel;
	private JDialog calibrateDialog, aboutDialog;

	private Task task;
	private Farm farmBot;

	private String currentFarmItem, currentFarmAction;
	private int currentCols, currentRows;

	private boolean taskStarted;

	private int selectedZoom, selectedDelay;
	private Point acceptPos;

	private Preferences prefs = Preferences.userNodeForPackage(this.getClass());
	private FarmJob job = null;

	protected File hotkeySupported = null;
	private String DLL_NAME = "JIntellitype.dll";

	private boolean hotkeyInvoked = false;

	private class FarmJob extends Thread {
		public void run() {
			try {
				farmBot.cancel = false;
				farmBot.work(currentFarmItem, currentFarmAction, currentRows,
						currentCols);
			} catch (Exception e) {
			}
		}

		public void end() {
			farmBot.cancel = true;
		}

	}

	private class Task extends SwingWorker<Void, Void> {
		private int progress;
		@Override
		public Void doInBackground() {

			while (!isInWorkingMode()) {
				Farm.sleep(100);
				if (!taskStarted)
					return null;
				remainingLabel.setText((hotkeySupported != null)
						? "Waiting WIN+S"
						: "Waiting CAPS LOCK");
			}

			progress = 0;
			farmBot.processed = progress;
			setProgress(progress);
			job = new FarmJob();
			job.start();

			while (farmBot.processed < farmBot.total) {
				Farm.sleep(100);
				if (!isInWorkingMode()) {
					job.end();
					return null;
				}
				progress = (int) Math.min(
						(farmBot.processed * 1.0 / farmBot.total) * 100, 100);
				setProgress(progress);
			}
			return null;
		}

		public void done() {
			hotkeyInvoked = false;
			progressBar.setIndeterminate(false);
			setProgress(100);
			taskStarted = false;
			startButton.setText("Start");
			enableInput();
		}
	}

	private boolean isInWorkingMode() {
		boolean mode = false;
		if (hotkeySupported == null) {
			mode = Toolkit.getDefaultToolkit().getLockingKeyState(ACTION_KEY);
		} else {
			mode = hotkeyInvoked;
		}
		return mode;
	}

	private class AboutDialog extends JDialog implements HyperlinkListener {
		private static final long serialVersionUID = 7456778689239730270L;
		private boolean hidden = true;
		private final String description = "A <a href='http://www.farmville.com/'>FarmVille</a> automator.<br/><br/>"
				+ " The application uses <a href='http://melloware.com/products/jintellitype'>JIntellitype</a>.<br/><br/>"
				+ "Copyright &copy;2009, <a href='http://code.google.com/p/farmvilleagrobot/'>Agrobot</a><br/>All rights reserved. ";

		public AboutDialog(final JFrame parent) {
			super(parent, "About");
			setLayout(new BorderLayout());
			setModal(true);
			setResizable(false);
			setDefaultCloseOperation(HIDE_ON_CLOSE);

			JPanel panel = new JPanel();
			panel.setLayout(new GridBagLayout());

			// components

			JLabel logoLabel = new JLabel(getResIcon("/res/logo.png"));
			JLabel titleLabel = new JLabel(APP_TITLE + " " + APP_VERSION);
			Font old = titleLabel.getFont();
			Font font = new Font(old.getName(), Font.BOLD, old.getSize() + 6);
			titleLabel.setFont(font);
			JEditorPane descText = new JEditorPane("text/html", description);
			descText.addHyperlinkListener(this);
			descText.setBackground(UIManager.getColor(logoLabel));
			descText.setEditable(false);
			JScrollPane scrollPane = new JScrollPane(descText);
			scrollPane.setPreferredSize(new Dimension(200, 125));
			scrollPane.setBorder(BorderFactory.createEmptyBorder());

			// layout
			GridBagConstraints c = new GridBagConstraints();
			c.insets = new Insets(0, 10, 10, 0);
			c.fill = GridBagConstraints.HORIZONTAL;
			c.anchor = GridBagConstraints.PAGE_START;

			GridBagConstraints ci = new GridBagConstraints();
			ci.insets = new Insets(0, 0, 10, 10);
			ci.gridheight = 2;
			ci.anchor = GridBagConstraints.FIRST_LINE_START;

			ci.gridx = 0;
			ci.gridy = 0;
			panel.add(logoLabel, ci);

			c.gridx = 1;
			c.gridy = 0;
			panel.add(titleLabel, c);

			c.gridx = 1;
			c.gridy = 1;
			panel.add(scrollPane, c);

			panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
			add(panel, BorderLayout.CENTER);
			pack();

			addWindowListener(new WindowAdapter() {
				public void windowClosing(WindowEvent we) {
					hidden = true;
				}

				public void windowActivated(WindowEvent we) {
					if (hidden) {
						setLocationRelativeTo(parent);
						hidden = false;
					}
				}
			});

		}

		public void browse(URI uri) {
			if (Desktop.isDesktopSupported()) {
				Desktop d = Desktop.getDesktop();
				if (d.isSupported(Action.BROWSE))
					try {
						d.browse(uri);
					} catch (IOException e) {
					}
			}
		}

		@Override
		public void hyperlinkUpdate(HyperlinkEvent e) {
			if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
				try {
					browse(e.getURL().toURI());
				} catch (URISyntaxException e1) {
				}

			}
		}
	}

	private class CalibrateDialog extends JDialog {

		private static final long serialVersionUID = 1980754325670729316L;
		private final String PRESSHINT = (hotkeySupported == null)
				? "Press CAPS LOCK"
				: "Press WIN+S";
		private final String RELEASEHINT = (hotkeySupported == null)
				? "Release CAPS LOCK when ready"
				: "Press WIN+S when ready";

		private JLabel cLabel, infoLabel;
		private JPanel panel;

		private Task task;

		private boolean calibrating = true;
		private boolean hidden = true;

		class Task extends SwingWorker<Void, Void> {
			@Override
			public Void doInBackground() {
				while (true) {
					if (!calibrating)
						return null;
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
					}
					if (isInWorkingMode()) {
						infoLabel.setText(RELEASEHINT);
						PointerInfo info = MouseInfo.getPointerInfo();
						acceptPos = info.getLocation();
						cLabel.setText(acceptPos.x + " x " + acceptPos.y);
					} else {
						infoLabel.setText(PRESSHINT);
					}
				}
			}
		}

		public CalibrateDialog(final JFrame parent) {
			super(parent);
			setLayout(new BorderLayout());
			setModal(true);
			setResizable(false);
			setDefaultCloseOperation(HIDE_ON_CLOSE);

			panel = new JPanel();
			panel.setLayout(new GridBagLayout());

			// components
			cLabel = new JLabel("0 x 0");
			infoLabel = new JLabel(PRESSHINT);

			// layout
			GridBagConstraints c = new GridBagConstraints();
			c.insets = new Insets(0, 0, 10, 0);
			c.fill = GridBagConstraints.HORIZONTAL;
			c.weightx = 0.5;
			c.anchor = GridBagConstraints.CENTER;

			c.gridx = 0;
			c.gridy = 0;
			panel.add(infoLabel, c);

			c.gridx = 0;
			c.gridy = 1;
			panel.add(cLabel, c);
			panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

			add(panel, BorderLayout.CENTER);
			pack();

			addWindowListener(new WindowAdapter() {
				public void windowClosing(WindowEvent we) {
					hidden = true;
					setAcceptPositionLabel();
					calibrating = false;
					taskStarted = false;
					task.cancel(true);
				}

				public void windowActivated(WindowEvent we) {
					if (hidden) {
						setLocationRelativeTo(parent);
						hidden = false;
						calibrating = true;
						taskStarted = true;
						task = new Task();
						task.execute();
					}
				}
			});
		}
	}

	private JMenuBar createMenuBar() {
		JMenuBar menuBar = new JMenuBar();

		JMenu menu;
		JMenuItem menuItem;

		menu = new JMenu("Advanced");
		menu.setMnemonic(KeyEvent.VK_A);
		menu.getAccessibleContext().setAccessibleDescription(
				"Advanced settings");
		menuBar.add(menu);

		JMenu submenu = new JMenu("Click delay");
		submenu.addActionListener(this);

		ButtonGroup group = new ButtonGroup();

		for (Integer delay : DELAYS) {
			menuItem = new JRadioButtonMenuItem("" + delay + " ms");
			if (delay == farmBot.delay) {
				menuItem.setSelected(true);
			}
			menuItem.setActionCommand("clickdelay_" + delay);
			menuItem.addActionListener(this);
			group.add(menuItem);
			submenu.add(menuItem);
		}
		menu.add(submenu);

		menuItem = new JMenuItem("Calibrate Accept");
		menuItem.setActionCommand("calibrate_accept");
		menuItem.addActionListener(this);
		menu.add(menuItem);

		menu = new JMenu("Help");
		menuBar.add(menu);
		menuItem = new JMenuItem("About");
		menuItem.setActionCommand("about_dialog");
		menuItem.addActionListener(this);
		menu.add(menuItem);

		return menuBar;
	}

	private void getCurrentFarmItem() {
		currentFarmItem = (String) farmItemsList.getSelectedItem();
	}

	private String[] getCurrentItemActions() {
		getCurrentFarmItem();
		String[] alist = {};
		try {
			Field a = this.getClass().getDeclaredField(
					(currentFarmItem + "_actions").toUpperCase());
			alist = (String[]) a.get(null);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return alist;
	}

	private void changeActions() {
		actionsList.removeAllItems();
		for (String item : getCurrentItemActions()) {
			actionsList.addItem(item);
		}
	}

	public FarmGui() throws AWTException {
		Init();
		farmBot = new Farm();
		loadPreferences();

		// components
		zoomList = new JComboBox(ZOOM_LEVELS);
		zoomList.setSelectedItem(selectedZoom);
		zoomList.setActionCommand("zoomlist");
		zoomList.addActionListener(this);

		farmItemsList = new JComboBox(FARM_ITEMS);
		farmItemsList.setSelectedItem(currentFarmItem);
		farmItemsList.setActionCommand("farmlist");
		farmItemsList.addActionListener(this);

		actionsList = new JComboBox(getCurrentItemActions());
		if (currentFarmAction == null) {
			actionsList.setSelectedIndex(0);
			currentFarmAction = (String) actionsList.getSelectedItem();
		} else {
			actionsList.setSelectedItem(currentFarmAction);
		}
		actionsList.addActionListener(this);
		actionsList.setActionCommand("actionlist");

		startButton = new JButton("Start");
		startButton.setVerticalTextPosition(AbstractButton.CENTER);
		startButton.setHorizontalTextPosition(AbstractButton.LEADING);
		startButton.setActionCommand("start");
		startButton.addActionListener(this);
		startButton.setToolTipText("Start!");
		taskStarted = false;

		rowsText = new JSpinner(ROWS_SPINNER_MODEL);
		rowsText.setValue(currentRows);
		ROWS_SPINNER_MODEL.addChangeListener(this);
		colsText = new JSpinner(COLS_SPINNER_MODEL);
		COLS_SPINNER_MODEL.addChangeListener(this);
		colsText.setValue(currentCols);

		remainingLabel = new JLabel("");

		progressBar = new JProgressBar(0, 100);
		progressBar.setStringPainted(true);

		acceptPositionLabel = new JLabel();
		setAcceptPositionLabel();
		clickDelayLabel = new JLabel();
		setClickDelayLabel();

		statusBar = new JPanel(new FlowLayout(FlowLayout.TRAILING, 3, 1));

		contentPanel = new JPanel(new GridBagLayout());
		contentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 0, 20));

		// layout
		GridBagConstraints c_list = new GridBagConstraints();
		c_list.gridwidth = 6;
		c_list.insets = new Insets(0, 0, 10, 0);
		c_list.weightx = 0.5;
		c_list.fill = GridBagConstraints.HORIZONTAL;

		GridBagConstraints c = new GridBagConstraints();
		c.insets = new Insets(0, 0, 10, 0);
		c.fill = GridBagConstraints.HORIZONTAL;

		GridBagConstraints c_text = new GridBagConstraints();
		c_text.insets = new Insets(0, 0, 10, 10);
		c_text.weightx = 0.5;
		c_text.fill = GridBagConstraints.HORIZONTAL;

		GridBagConstraints c_remaining = new GridBagConstraints();
		c_remaining.insets = new Insets(0, 0, 0, 10);
		c_remaining.weightx = 0.5;
		c_remaining.gridwidth = 5;
		c_remaining.fill = GridBagConstraints.HORIZONTAL;

		c_list.gridx = 0;
		c_list.gridy = 0;
		contentPanel.add(farmItemsList, c_list);

		c_list.gridx = 0;
		c_list.gridy = 1;
		contentPanel.add(actionsList, c_list);

		c.gridx = 0;
		c.gridy = 2;
		contentPanel.add(ROWS_LABEL, c);

		c_text.gridx = 1;
		c_text.gridy = 2;
		contentPanel.add(rowsText, c_text);

		c.gridx = 2;
		c.gridy = 2;
		contentPanel.add(COLS_LABEL, c);

		c_text.gridx = 3;
		c_text.gridy = 2;
		contentPanel.add(colsText, c_text);

		c.gridx = 4;
		c.gridy = 2;
		contentPanel.add(ZOOM_LABEL, c);

		c_text.gridx = 5;
		c_text.gridy = 2;
		c_text.insets = new Insets(0, 0, 10, 0);
		contentPanel.add(zoomList, c_text);

		c.gridx = 0;
		c.gridy = 3;
		contentPanel.add(STATUS_LABEL, c);

		c_list.gridx = 0;
		c_list.gridy = 4;
		contentPanel.add(progressBar, c_list);

		c_remaining.gridx = 0;
		c_remaining.gridy = 5;
		contentPanel.add(remainingLabel, c_remaining);

		c.gridx = 5;
		c.gridy = 5;
		c_list.insets = new Insets(0, 0, 0, 0);
		contentPanel.add(startButton, c);

		statusBar.add(acceptPositionLabel);
		statusBar.add(clickDelayLabel);

		JPanel panel = new JPanel(new BorderLayout());
		panel.setOpaque(true);
		panel.add(contentPanel, BorderLayout.CENTER);
		panel.add(statusBar, BorderLayout.SOUTH);

		frame = new JFrame(APP_TITLE + " " + APP_VERSION);
		frame.addWindowListener(this);
		frame.setJMenuBar(createMenuBar());
		setAppIcon();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setContentPane(panel);
		frame.setMinimumSize(new Dimension(300, 200));
		frame.setResizable(false);
		frame.pack();
		frame.setAlwaysOnTop(true);
		frame.setVisible(true);
		calibrateDialog = new CalibrateDialog(frame);
		aboutDialog = new AboutDialog(frame);
	}

	private File getJNILocation() throws UnsatisfiedLinkError {
		File dllFile;
		try {
			dllFile = File.createTempFile("jintellitype", ".dll");
			dllFile.deleteOnExit();
		} catch (IOException e1) {
			return null;
		}
		InputStream in = FarmGui.class.getResourceAsStream("/res/" + DLL_NAME);
		if (in == null) {
			if (!dllFile.exists()) {
				throw new UnsatisfiedLinkError(DLL_NAME
						+ " : unable to find in ");
			}
		} else {
			if (dllFile.exists() && dllFile.canWrite()) {
				dllFile.delete();
			}
			if (!dllFile.exists()) {
				byte[] buffer = new byte[512];
				BufferedOutputStream out = null;
				try {
					try {
						out = new BufferedOutputStream(new FileOutputStream(
								dllFile));
						while (true) {
							int readed = in.read(buffer);
							if (readed > -1) {
								out.write(buffer, 0, readed);
							} else {
								break;
							}
						}
					} finally {
						if (out != null) {
							out.close();
						}
					}
				} catch (IOException e) {
					throw new UnsatisfiedLinkError("Can't write library in "
							+ dllFile);
				}
			}
			return dllFile;
		}
		return null;
	}

	private void Init() {
		if (System.getProperty("os.name").toLowerCase().startsWith("windows")
				|| System.getProperty("os.arch").equals("x86")) {
			hotkeySupported = getJNILocation();
			if (hotkeySupported != null) {
				try {
					JIntellitype.setLibraryLocation(hotkeySupported
							.getAbsolutePath());
					JIntellitype.getInstance().addHotKeyListener(this);
					JIntellitype.getInstance().registerHotKey(1,
							JIntellitype.MOD_WIN, (int) 'S');
					JIntellitype.getInstance().registerHotKey(2,
							JIntellitype.MOD_WIN, (int) 'A');
				} catch (Exception e) {
					hotkeySupported = null;
				};
			}
		}

	}

	private void loadPreferences() {
		selectedZoom = prefs.getInt("selectedZoom", ZOOM_LEVELS[0]);
		selectedDelay = prefs.getInt("selectedDelay", DELAYS[0]);
		currentFarmItem = prefs.get("currentFarmItem", FARM_ITEMS[0]);
		currentFarmAction = prefs.get("currentFarmAction", null);
		currentRows = prefs.getInt("currentRows", 1);
		currentCols = prefs.getInt("currentCols", 1);
		int x = prefs.getInt("acceptX", farmBot.acceptPos.x);
		int y = prefs.getInt("acceptY", farmBot.acceptPos.y);
		acceptPos = new Point(x, y);
	}

	private void savePreferences() {
		prefs.putInt("selectedDelay", selectedDelay);
		prefs.putInt("selectedZoom", selectedZoom);
		prefs.putInt("acceptX", acceptPos.x);
		prefs.putInt("acceptY", acceptPos.y);
		prefs.put("currentFarmItem", currentFarmItem);
		prefs.put("currentFarmAction", currentFarmAction);
		prefs.putInt("currentRows", currentRows);
		prefs.putInt("currentCols", currentCols);
	}

	private void setAcceptPositionLabel() {
		acceptPositionLabel.setText("Accept: " + acceptPos.x + "x"
				+ acceptPos.y);
	}

	private void startOperation() {
		if (isInWorkingMode()) {
			remainingLabel.setText("Release CAPS LOCK");
			return;
		}
		disableInput();
		taskStarted = true;
		hotkeyInvoked = false;
		progressBar.setIndeterminate(true);
		farmBot.total = currentCols * currentRows;
		farmBot.zoom = selectedZoom - 1;
		farmBot.delay = selectedDelay;
		farmBot.acceptPos = acceptPos;
		task = new Task();
		task.addPropertyChangeListener(this);
		task.execute();
		startButton.setText("Stop");
	}

	private void stopOperation() {
		taskStarted = false;
		hotkeyInvoked = false;
		task.cancel(true);
		progressBar.setIndeterminate(false);
		remainingLabel.setText("");
		startButton.setText("Start");
		enableInput();
	}

	public void actionPerformed(ActionEvent e) {
		String cmd = e.getActionCommand();
		if ("start".equals(cmd)) {
			try {
				if (!taskStarted) {
					startOperation();
				} else {
					stopOperation();
				}
			} catch (Exception ex) {
			}
		} else if ("farmlist".equals(cmd)) {
			changeActions();
		} else if ("actionlist".equals(cmd)) {
			currentFarmAction = (String) actionsList.getSelectedItem();
		} else if ("zoomlist".equals(cmd)) {
			selectedZoom = (Integer) zoomList.getSelectedItem();
		} else if (cmd.startsWith("clickdelay_")) {
			selectedDelay = new Integer(cmd.substring("clickdelay_".length()));
			setClickDelayLabel();
		} else if (cmd.equals("calibrate_accept")) {
			calibrate_accept();
		} else if (cmd.equals("about_dialog")) {
			aboutDialog.setVisible(true);
		}
	}

	private void controlInput(boolean enabled) {
		zoomList.setEnabled(enabled);
		farmItemsList.setEnabled(enabled);
		rowsText.setEnabled(enabled);
		colsText.setEnabled(enabled);
		actionsList.setEnabled(enabled);
	}

	private void enableInput() {
		controlInput(true);
	}

	private void disableInput() {
		controlInput(false);
	}

	private void setClickDelayLabel() {
		clickDelayLabel.setText("Click: " + farmBot.delay + "ms");
	}

	private void calibrate_accept() {
		calibrateDialog.pack();
		calibrateDialog.setVisible(true);
	}

	public void propertyChange(PropertyChangeEvent evt) {
		if ("progress" == evt.getPropertyName()) {
			progressBar.setIndeterminate(false);
			int progress = (Integer) evt.getNewValue();
			progressBar.setValue(progress);
			progressBar.setString("" + progress + "%");
			remainingLabel.setText(currentFarmAction + " " + currentFarmItem
					+ " " + farmBot.processed + "/" + farmBot.total);
		}
	}

	private Image getResImage(String path) {
		URL imageURL = FarmGui.class.getResource(path);
		return (imageURL != null) ? new ImageIcon(imageURL).getImage() : null;
	}

	private ImageIcon getResIcon(String path) {
		Image image = getResImage(path);
		return (image != null) ? new ImageIcon(image) : null;
	}

	private void setAppIcon() {
		Image icon = getResImage("/res/appicon.png");
		if (icon != null) {
			frame.setIconImage(icon);
		}
	}

	private static void createAndShowGUI() throws AWTException {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {
		}

		JFrame.setDefaultLookAndFeelDecorated(true);
		JDialog.setDefaultLookAndFeelDecorated(true);

		new FarmGui();
	}

	public static void main(String[] args) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				try {
					createAndShowGUI();
				} catch (AWTException e) {
				}
			}
		});
	}

	@Override
	public void windowActivated(WindowEvent e) {
	}

	@Override
	public void windowClosed(WindowEvent e) {
	}

	@Override
	public void windowClosing(WindowEvent e) {
		if (hotkeySupported != null) {
			JIntellitype.getInstance().cleanUp();
		}
		savePreferences();
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
	}

	@Override
	public void windowIconified(WindowEvent e) {
	}

	@Override
	public void windowOpened(WindowEvent e) {
	}

	@Override
	public void stateChanged(ChangeEvent e) {
		Object source = e.getSource();
		if (source instanceof SpinnerNumberModel) {
			SpinnerNumberModel model = (SpinnerNumberModel) source;
			if (source.equals(ROWS_SPINNER_MODEL)) {
				currentRows = (Integer) model.getValue();
			} else if (source.equals(COLS_SPINNER_MODEL)) {
				currentCols = (Integer) model.getValue();
			}
		}

	}

	@Override
	public void onHotKey(int identifier) {
		if (identifier == 1 && taskStarted) {
			hotkeyInvoked = !hotkeyInvoked;
		} else if (identifier == 2 && !taskStarted) {
			startOperation();
		}
	}
}
